/*********************************************************************************
 *      Copyright:  (C) 2025 chenhongyu IOT STUDY
 *                  All rights reserved.
 *
 *       Filename:  bluez.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(17/07/25)
 *         Author:  chenhongyu <chenhongyu0822@163.com>
 *      ChangeLog:  1, Release initial version on "17/07/25 21:43:30"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dbus/dbus.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include <syslog.h>

#include "bluez.h"
#include "main.h"

#define   BLUEZ_BUS_NAME                "org.bluez"
#define   ADAPTER_PATH                  "/org/bluez/hci0"
#define   DEVICE_MAC                    "A0_76_4E_57_06_16"
#define   DEVICE_PATH                   "/org/bluez/hci0/dev_" DEVICE_MAC
#define   CHARACTERISTIC_PATH           DEVICE_PATH "/service0028/char0037"
#define   CHAR_PATH                     DEVICE_PATH "/service0028/char0035"

int print_notify_value(DBusMessageIter *variant_iter, uint8_t *buf)
{
    DBusMessageIter                array_iter;
    int                            arg_type;
    uint8_t                        byte;
	size_t                         index = 0;
    
	dbus_message_iter_recurse(variant_iter, &array_iter);
    syslog(LOG_INFO, "Notification: ");
	while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID)
    {
        arg_type = dbus_message_iter_get_arg_type(&array_iter);
        if (arg_type == DBUS_TYPE_BYTE)
        {
            dbus_message_iter_get_basic(&array_iter, &byte);
            
			buf[index++] =byte;
			
			if (byte >= 32 && byte <= 126)
            {
                syslog(LOG_INFO, "%c", byte);
            }
            else
            {
                syslog(LOG_INFO, ".");
            }
        }
        dbus_message_iter_next(&array_iter);
    }
 
	syslog(LOG_INFO, "\n");
	
	return index;
}

int handle_properties_changed(DBusMessage *msg, uint8_t *buf)
{
    DBusMessageIter                 args;
    const char                      *iface;
    DBusMessageIter                 changed_props;
    DBusMessageIter                 entry;
    const char                      *key;
    DBusMessageIter                 variant_iter;
	int                             len;

    dbus_message_iter_init(msg, &args);

    dbus_message_iter_get_basic(&args, &iface);
    dbus_message_iter_next(&args);

    dbus_message_iter_recurse(&args, &changed_props);

    while (dbus_message_iter_get_arg_type(&changed_props) != DBUS_TYPE_INVALID)
    {
        dbus_message_iter_recurse(&changed_props, &entry);

        dbus_message_iter_get_basic(&entry, &key);

        if (strcmp(key, "Value") == 0)
        {
            dbus_message_iter_next(&entry);
            dbus_message_iter_recurse(&entry, &variant_iter);
            len= print_notify_value(&variant_iter, buf);
        }
        dbus_message_iter_next(&changed_props);
    }
	return len;
}

int call_method(DBusConnection *conn, const char *path, const char *interface, const char *method)
{
    DBusMessage              *msg, *reply;
    DBusError                err;

    dbus_error_init(&err);

    msg = dbus_message_new_method_call(BLUEZ_BUS_NAME, path, interface, method);
    if (!msg) 
	{
		syslog(LOG_ERR, "D-Bus message creation failed:%s.\n", strerror(errno));
		return -1;
	}

    reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);

    dbus_message_unref(msg);

    if (dbus_error_is_set(&err))
    {
        syslog(LOG_ERR, "D-Bus call %s failed: %s.\n", method, err.message);
        dbus_error_free(&err);
        return -2;
    }

    if (reply)
	{
        dbus_message_unref(reply);
	}

    return 0;
}


int send_command(DBusConnection *conn, const char* char_path, const char* cmd_str)
{
    DBusMessage                          *msg, *reply;
    DBusMessageIter                      args, array_iter, options_iter;
    DBusError                            err;
	uint8_t                              byte_value;

	dbus_error_init(&err);

    msg = dbus_message_new_method_call("org.bluez", char_path,
                                       "org.bluez.GattCharacteristic1", "WriteValue");
    if (!msg)
    {
		syslog(LOG_ERR, "D-Bus message creation failed: %s.\n", strerror(errno));
        return -1;
    }

    dbus_message_iter_init_append(msg, &args);

    dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "y", &array_iter);

    for (size_t i = 0; i < strlen(cmd_str); ++i)
    {
        byte_value = (uint8_t)cmd_str[i];
        dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_BYTE, &byte_value);
    }
    
	dbus_message_iter_close_container(&args, &array_iter);

    dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &options_iter);
    dbus_message_iter_close_container(&args, &options_iter);

    reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
    
	dbus_message_unref(msg);

    if (dbus_error_is_set(&err))
    {
		syslog(LOG_ERR, "WriteValue failed: %s\n", err.message);
        dbus_error_free(&err);
        return -1;
    }

    syslog(LOG_INFO, "Command sent: %s\n", cmd_str);
    dbus_message_unref(reply);
    
	return 0;
}

void *bluetooth_thread(void *arg)
{
	ble_ctx              *ble=&((bled_t *)arg)->ble;
	uint8_t              buf[256];
	DBusMessage          *msg;
	DBusError            err;
	int                  len;

	ble->notify_char_path=CHARACTERISTIC_PATH;
	ble->cmd_char_path=CHAR_PATH;

	dbus_error_init(&err);
    ble->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if (dbus_error_is_set(&err)) 
	{
    	syslog(LOG_ERR, "D-Bus Connection Error: %s\n", err.message);
        dbus_error_free(&err);
		return NULL;
    }
	syslog(LOG_INFO, "D-Bus system bus acquired successfully.\n");
	
	syslog(LOG_INFO, "Connecting to BLE device...\n");
    if (call_method(ble->conn, DEVICE_PATH, "org.bluez.Device1", "Connect")<0)
    {
		syslog(LOG_ERR, "Connect to ble failure:%s.\n", strerror(errno));
		return NULL;
    }
	syslog(LOG_INFO, "Successfully connected to BLE device.\n");

    sleep(5);

	syslog(LOG_INFO, "Enabling notifications...\n");
    if (call_method(ble->conn, ble->notify_char_path, "org.bluez.GattCharacteristic1", "StartNotify")<0)
    {
        syslog(LOG_ERR, "Failed to enable notifications:%s.\n", strerror(errno));
		return NULL;
    }
	syslog(LOG_INFO,"Notifications enabled successfully on characteristic: %s\n", ble->notify_char_path);

	dbus_bus_add_match(ble->conn,
			"type='signal', interface='org.freedesktop.DBus.Properties', member='PropertiesChanged'",
			&err);
	dbus_connection_flush(ble->conn);
	
	while(1)
	{
		dbus_connection_read_write(ble->conn, 0);
		msg=dbus_connection_pop_message(ble->conn);
		if (!msg)
		{
			syslog(LOG_DEBUG, "No new D-Bus messages, waiting...\n");
    		usleep(100000);
    		continue;
		}

		if(dbus_message_is_signal(msg, "org.freedesktop.DBus.Properties", "PropertiesChanged")&&
		strstr(dbus_message_get_path(msg), ble->notify_char_path))
		{
			memset(buf, 0, sizeof(buf));
			len=handle_properties_changed(msg, buf);
			if(len>0)
			{
				syslog(LOG_INFO, "Received BLE notification: %s\n", buf);
				memset(ble->recv_buf, 0, sizeof(ble->recv_buf));
				memcpy(ble->recv_buf, buf, len);
				ble->recv_len = len;
			}
			else
			{
				syslog(LOG_WARNING, "Received BLE notification but no data was parsed.\n");
			}
		}

		dbus_message_unref(msg);

		if(ble->has_cmd)
		{
			if(send_command(ble->conn, ble->cmd_char_path, ble->cmd_buf)<0)
			{
				syslog(LOG_ERR, "Failed to send command to BLE device: %s\n", ble->cmd_buf);
			}
			else
			{
				syslog(LOG_INFO, "Command sent successfully: %s\n", ble->cmd_buf);
			}

			ble->has_cmd=0;
		}
		usleep(10000);
	}
	return NULL;
}
